/*

	AUTHOR: aeroson
	NAME: set_loadout.sqf
	VERSION: 4.1
	
	DOWNLOAD & PARTICIPATE:
	https://github.com/aeroson/a3-loadout
	http://forums.bistudio.com/showthread.php?148577-GET-SET-Loadout-(saves-and-loads-pretty-much-everything)
	
	DESCRIPTION:
	These scripts allows you set/get (load/save)all of the unit's gear, including:
	uniform, vest, backpack, contents of it, all quiped items, all three weapons with their attachments, currently loaded magazines and number of ammo in magazines
	Useful for saving/loading loadouts. 
	Ideal for revive scripts where you have to set exactly the same loadout to newly created unit.
	Uses workaround with placeholders to add vest/backpack items, so items stay where you put them.
	
	PARAMETER(S):
	0 : target unit
	1 : array of strings/arrays containing desired target unit's loadout, obtained from fnc_get_loadout.sqf
	2 : (optional) array of options, default [] : ["ammo"]  will allow loading of partially emptied magazines, otherwise magazines will be full 	 	
	
	addAction support:
	Sets player's loadout from global var loadout
  
*/

private ["_target","_options","_loadMagsAmmo","_data","_loadedMagazines","_placeholderCount","_loadBeforeAdd","_add","_outfit","_addWeapon","_addPrimary","_addHandgun","_addSecondary","_addOrder","_currentWeapon","_currentMode"];

_options = [];

// addAction support
if(count _this < 4) then {
	#define PARAM_START private ["_PARAM_INDEX"]; _PARAM_INDEX=0;
	#define PARAM_REQ(A) if (count _this <= _PARAM_INDEX) exitWith { systemChat format["required param '%1' not supplied in file:'%2' at line:%3", #A ,__FILE__,__LINE__]; }; A = _this select _PARAM_INDEX; _PARAM_INDEX=_PARAM_INDEX+1;
	#define PARAM(A,B) A = B; if (count _this > _PARAM_INDEX) then { A = _this select _PARAM_INDEX; }; _PARAM_INDEX=_PARAM_INDEX+1;
	PARAM_START
	PARAM_REQ(_target)
	PARAM_REQ(_data)
	PARAM(_options,[])
} else {
	_target = player;
	_data = loadout;
	//playSound3D ["A3\Sounds_F\sfx\ZoomIn.wav", _target]; 
};

if(isNil{_data}) exitWith {
	systemChat "you are trying to set/load empty loadout";
};
if(count _data < 13) exitWith {
	systemChat "you are trying to set/load corrupted loadout";
};

#define QUOTE(A) #A
#define EL(A,B) ((A) select (B))
#define _THIS(A) EL(_this,A)

// placeholders
#define PLACEHOLDER_BACKPACK QUOTE(B_Kitbag_mcamo) // any backpack with capacity>0
#define PLACEHOLDER_ITEM QUOTE(ItemWatch) // addItem placeholder should be smallest item possible

_loadMagsAmmo = "ammo" in _options;
_loadedMagazines = [];
if(count _data > 13) then {
	if(typeName(_data select 13)=="ARRAY") then {
		_loadedMagazines = _data select 13;
	};
};

_currentWeapon = "";
if(count _data > 14) then {
	_currentWeapon = _data select 14;
};
_currentMode = ""; 
if(count _data > 15) then {
	_currentMode = _data select 15;
};	

_placeholderCount = 0;

// basic add function intended for use with uniform and vest
_add = {
	private ["_target","_item"];
	_target = _this select 0;
	_item = _this select 1;
	if(typename _item == "ARRAY") then {
		if(_item select 0 != "") then {
			if(_loadMagsAmmo) then {
				_target addMagazine _item;
			} else {
				_target addMagazine (_item select 0);
			};
		};
	} else {
		if(_item != "") then {
			if(isClass(configFile>>"CfgMagazines">>_item)) then {
				_target addMagazine _item;
			} else {
				if(isClass(configFile>>"CfgWeapons">>_item>>"WeaponSlotsInfo") && getNumber(configFile>>"CfgWeapons">>_item>>"showempty")==1) then {
					_target addWeapon _item;  
				} else {
					_target addItem _item;        
				};
			};
		};
	};
};

// remove clothes to prevent incorrect mag loading
removeUniform _target;
removeVest _target;
removeBackpack _target;

_outfit = PLACEHOLDER_BACKPACK; // we need to add items somewhere before we can assign them
_target addBackpack _outfit;
clearAllItemsFromBackpack _target;
removeAllAssignedItems _target;

// add loaded magazines of assigned items
if(count _loadedMagazines>=3) then {
	{ 
		[_target,_x] call _add;
	} forEach (_loadedMagazines select 3);
};

// add assigned items
{ 
	[_target,_x] call _add;
	_target assignItem _x;
} forEach (_data select 0);

// universal add weapon to hands
_addWeapon = {
	private ["_weapon","_magazines","_muzzles"];
	clearAllItemsFromBackpack _target;
	_target removeWeapon ([] call _THIS(0));
	_weapon = _data select _THIS(1);     
	if(_weapon != "") then {
		if(isClass(configFile>>"CfgWeapons">>_weapon)) then {
			if (_currentWeapon == "") then {
				_currentWeapon = _weapon;
			}; 
			if(count _loadedMagazines > 0) then {
				_magazines = _loadedMagazines select _THIS(5); // get loaded magazines from saved loadout				
				if(typename _magazines != "ARRAY") then { // backwards compatibility, make sure _magazines is array
					if(_magazines=="") then { 
						_magazines = [];
					} else {
						_magazines = [_magazines];
					};
				};
			} else {          
				_magazines = [getArray(configFile>>"CfgWeapons">>_weapon>>"magazines") select 0]; // generate weapon magazine
				_muzzles = configFile>>"CfgWeapons">>_weapon>>"muzzles";
				if(isArray(_muzzles)) then { // generate magazine for each muzzle	
					{
						if (_x != "this") then {
							_magazines set [count _magazines, toLower(getArray(configFile>>"CfgWeapons">>_weapon>>_x>>"magazines") select 0)];
						};
					} forEach getArray(_muzzles);	
				};
			};        	  
			{
				[_target, _x] call _add;
			} forEach _magazines; // add magazines							
			_target addWeapon _weapon;                                                                                                                
			{ 
				if(_x!="" && !(_x in ([] call _THIS(3)))) then { 
					_x call _THIS(4); 
				}; 
			} forEach (_data select (1+_THIS(1))); // add weapon items
		} else {
			systemchat format["%1 %2 doesn't exist",_THIS(2),_weapon];
			if (_currentWeapon == _weapon) then {
				_currentWeapon = "";
				_currentMode = "";
			};
		};                             											                                                                                               
	};
};

// add primary weapon, add primary weapon loaded magazine, add primary weapon items
_addPrimary = {
	[
		{ primaryWeapon _target }, // 0 // get current weapon
		1, // 1 //weapon classname index in _data
		"primary", // 2 // weapon debug type
		{ primaryWeaponItems _target }, // 3 // weapon items
		{ _target addPrimaryWeaponItem _this }, // 4 // add weapon item
		0 // 5 // index in _loadedMagazines
	] call _addWeapon;
};

// add handgun weapon, add handgun weapon loaded magazine, add handgun weapon items
_addHandgun = {
	[
		{ handgunWeapon _target }, // 0 // get current weapon
		3, // 1 //weapon classname index in _data
		"handgun", // 2 // weapon debug type
		{ handgunItems _target }, // 3 // weapon items
		{ _target addHandgunItem _this }, // 4 // add weapon item
		1 // 5 // index in _loadedMagazines
	] call _addWeapon;
};
      
// add secondary weapon, add secondary weapon loaded magazine, add secondary weapon items
_addSecondary = {
	[
		{ secondaryWeapon _target }, // 0 // get current weapon
		5, // 1 //weapon classname index in _data in _data
		"secondary", // 2 // weapon debug type
		{ secondaryWeaponItems _target }, // 3 // weapon items
		{ _target addSecondaryWeaponItem _this }, // 4 // add weapon item
		2 // 5 // index in _loadedMagazines
	] call _addWeapon;
};


// first added weapon is selected, order add functions to firstly add currently selected weapon
_addOrder=[_addPrimary,_addHandgun,_addSecondary];
if(_currentWeapon!="") then {
	_addOrder = switch _currentWeapon do { 
		case (_data select 3): {[_addHandgun,_addPrimary,_addSecondary]};
		case (_data select 5): {[_addSecondary,_addPrimary,_addHandgun]};
		default {_addOrder};  
	};			
};
{
	[] call _x;
} forEach _addOrder;

// select weapon and firing mode
if(vehicle _target == _target && _currentWeapon != "" && _currentMode != "") then {
	_muzzles = 0;                                                                                                           
	while { (_currentWeapon != currentMuzzle _target || _currentMode != currentWeaponMode _target ) && _muzzles < 100 } do {
		_target action ["SWITCHWEAPON", _target, _target, _muzzles];
		_muzzles = _muzzles + 1;
	};
	if(_muzzles >= 100) then {
		systemchat format["mode %1 for %2 doesn't exist", _currentMode, _currentWeapon];
		_currentMode = "";		
	};
} else {
	_currentMode = "";
};
if(_currentMode == "" && _currentWeapon != "") then {
	_target selectWeapon _currentWeapon;
};

clearAllItemsFromBackpack _target;

// add uniform, add uniform items and fill uniform with item placeholders
_outfit = _data select 7;  
if(_outfit != "") then {
	if(isClass(configFile>>"CfgWeapons">>_outfit)) then {			
		_target forceAddUniform _outfit;
		_target addItem PLACEHOLDER_ITEM;
		if(loadUniform _target > 0) then {
			_placeholderCount = _placeholderCount + 1;
			{ 
				[_target,_x] call _add; 
			} forEach (_data select 8);			
			while{true} do {
				_loadBeforeAdd = loadUniform _target;
				_target addItem PLACEHOLDER_ITEM;
				if(loadUniform _target == _loadBeforeAdd) exitWith {};
				_placeholderCount = _placeholderCount + 1;					
			};		
		};
	} else {
		systemchat format["uniform %1 doesn't exist",_outfit];
	};		
};

// add vest, add vest items and fill vest with item placeholders
_outfit = _data select 9; 
if(_outfit != "") then {
	if(isClass(configFile>>"CfgWeapons">>_outfit)) then {
		_target addVest _outfit;
		_target addItem PLACEHOLDER_ITEM;
		if(loadVest _target > 0) then {
			_placeholderCount = _placeholderCount + 1;	
			{ 
				[_target,_x] call _add;
			} forEach (_data select 10);
			while{true} do {
				_loadBeforeAdd = loadVest _target;
				_target addItem PLACEHOLDER_ITEM;
				if(loadVest _target == _loadBeforeAdd) exitWith {};
				_placeholderCount = _placeholderCount + 1;	
			};				
		};
	} else {
		systemchat format["vest %1 doesn't exist",_outfit];
	};
};      
 
// more complex add function intended for use with backpack
_add = {
	private ["_target","_item"];
	_target = _this select 0;
	_item = _this select 1;
	if(typename _item == "ARRAY") then {
		if(_item select 0 != "") then {
			if(_loadMagsAmmo) then {
				_target addMagazine _item;
			} else {
				_target addMagazine (_item select 0);
			};
		};
	} else {
		if(isClass(configFile>>"CfgMagazines">>_item)) then {
			(unitBackpack _target) addMagazineCargo [_item,1];
		} else {
			if(_item != "") then {
				if(getNumber(configFile>>"CfgVehicles">>_item>>"isbackpack")==1) then {
					(unitBackpack _target) addBackpackCargo [_item,1];  
				} else {
					if(isClass(configFile>>"CfgWeapons">>_item>>"WeaponSlotsInfo") && getNumber(configFile>>"CfgWeapons">>_item>>"showempty")==1) then {
						(unitBackpack _target) addWeaponCargo [_item,1];  
					} else {
						_target addItem _item;         
					};
				};
			};
		};
	};       
};     

// add backpack and add backpack items
removeBackpack _target;
_outfit = _data select 11; 
if(_outfit != "") then {
	if(getNumber(configFile>>"CfgVehicles">>_outfit>>"isbackpack")==1) then {
		_target addBackpack _outfit;                                                                    
		clearAllItemsFromBackpack _target;
		_target addItem PLACEHOLDER_ITEM;
		_placeholderCount = _placeholderCount + 1;
		if(loadBackpack _target > 0) then {		
			{
				[_target, _x] call _add;
			} forEach (_data select 12);
		};
	} else {
		systemchat format["backpack %1 doesn't exist",_outfit];
	};
};

// remove item placeholders
for "_i" from 1 to _placeholderCount do {
	_target removeItem PLACEHOLDER_ITEM; 
};


// make loadout visible fix?
if(vehicle _target == _target) then {
	//_target switchMove (getArray(configFile>>"CfgMovesMaleSdr">>"States">>animationState player>>"ConnectTo") select 0);
	_target setPosATL (getPosATL _target);
};